<?php

namespace App\Models;

use App\Exceptions\CouponCodeUnavailableException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

// 优惠劵
class CouponCode extends Model
{
    // 用常量的方法定义支持的优惠劵类型
    const TYPE_FIXED = 'fixed';
    const TYPE_PERCENT = 'percent';

    public static $typeMap = [
        self::TYPE_FIXED => '固定金额',
        self::TYPE_PERCENT => '比例'
    ];

    // 用常量的方法定义支持的优惠劵类型
    const COUPON_CODE_ABLE_BRAND = Brand::class;
    const COUPON_CODE_ABLE_PRODUCT = Product::class;
    const COUPON_CODE_ABLE_CATEGORY = Category::class;

    public static $couponCodeAbleMap = [
        self::COUPON_CODE_ABLE_BRAND => '限品牌',
        self::COUPON_CODE_ABLE_PRODUCT => '限商品',
        self::COUPON_CODE_ABLE_CATEGORY => '限分类'
    ];

    protected $fillable = [
        'name',
        'code',
        'type',
        'value',
        'total',
        'used',
        'min_amount',
        'not_before',
        'not_after',
        'enabled',
        'max_fetch',
        'coupon_codeable_type',
        'coupon_codeable_id'
    ];

    protected $casts = [
        'enable' => 'boolean'
    ];

    protected $dates = [
        'not_before',
        'not_after'
    ];

    // 关联限定商品
    public function couponCodeable()
    {
        return $this->morphTo();
    }

    public static function findAvailableCode($length = 16)
    {
        do {
            $code = strtoupper(Str::random(16));
        } while (self::query()->where('code', $code)->exists());

        return $code;
    }

    public function getDescriptionAttribute()
    {
        $str = '';
        if ($this->min_amount > 0) {
            $str = "满" . str_replace('.00', '', $this->min_amount);
        }
        if ($this->type === self::TYPE_PERCENT) {
            return $str . '优惠' . str_replace('.00', '', $this->value) . '%';
        }

        return $str . '减' . str_replace('.00', '', $this->value);
    }

    // 校验优惠券是否有效 (一个用户只能使用一种优惠券一次)
    public function checkAvailable(User $user, $orderAmount = null)
    {

        // 如果优惠券没有启动
        if (!$this->enabled) {
            throw new CouponCodeUnavailableException('优惠券已过期');
        }

        if ($this->total - $this->used <= 0) {
            throw new CouponCodeUnavailableException('该优惠券已兑完');
        }

        if ($this->not_before && $this->not_before->gt(Carbon::now())) {
            throw new CouponCodeUnavailableException('优惠券未到生效日期');
        }

        if ($this->not_after && $this->not_after->lt(Carbon::now())) {
            throw new CouponCodeUnavailableException('优惠券已过期');
        }

        if (!is_null($orderAmount) && $orderAmount < $this->min_amount) {
            throw new CouponCodeUnavailableException('订单金额不满足该优惠券的最低金额');
        }

        // 未付款且未关闭订单    或者   已付款且未退款成功订单
        $used = Order::query()->where('user_id', $user->id)
            ->where('coupon_code_id', $this->id)
            ->where(function ($query) {
                $query->where(function ($query) {
                    $query->whereNull('paid_at')->where('closed', false);
                })->orWhere(function ($query) {
                    $query->whereNotNull('paid_at')->where('refund_status', '!=', Order::REFUND_STATUS_SUCCESS);
                });
            })
            ->exists();

        if ($used) {
            throw new CouponCodeUnavailableException('你已经使用过这张优惠券了');
        }

    }

    // 校验优惠券是否有效 , 返回boolean
    public function checkAvailableReturnBoolean($user, $total_amount)
    {
        if (!$this->enabled) {
            return false;
        }

        if ($this->not_before && $this->not_before->gt(Carbon::now())) {
            return false;
        }

        if ($this->not_after && $this->not_after->lt(Carbon::now())) {
            return false;
        }

        if ($this->total - $this->used <= 0) {
            return false;
        }

        if (!is_null($total_amount) && $total_amount < $this->min_amount) {
            return false;
        }

        $used = Order::query()->where('user_id', $user->id)
            ->where('coupon_code_id', $this->id)
            ->where(function ($query) {
                $query->where(function ($query) {
                    $query->whereNull('paid_at')->where('closed', false);
                })->orWhere(function ($query) {
                    $query->whereNotNull('paid_at')->where('refund_status', '!=', Order::REFUND_STATUS_SUCCESS);
                });
            })
            ->exists();

        if ($used) {
            return false;
        }
        return true;
    }

    // 计算优惠后的金额
    public function getAdjustedPrice($orderAmount)
    {
        // 固定金额
        if ($this->type === self::TYPE_FIXED) {
            return max(0.01, $orderAmount - $this->value);
        }

        return number_format($orderAmount * (100 - $this->value) / 100, 2, '.', '');
    }

    public function changeUsed($increase = true)
    {
        if ($increase) {
            return $this->where('id', $this->id)->where('used', '<', $this->total)->increment('used');
        } else {
            return $this->decrement('used');
        }
    }
}
